package de.popforge.audio
{
	import __AS3__.vec.Vector;
	
	import de.popforge.audio.driver.AudioDriver;
	import de.popforge.audio.driver.IAudioProcessor;		

	/**
	 * AS3 (non-optimized) port of the popular Freeverb plugin
	 * 
	 * http://music.columbia.edu/pipermail/music-dsp/attachments/20011014/dd63c4ba/freeverbsource-0001.bin
	 * 
	 * @author Andre Michelle
	 */
	
	public final class Freeverb
		implements IAudioProcessor
	{
		private const gain: Number = 0.02;
		
		private const combtuning1: int = 1116;
		private const combtuning2: int = 1188;
		private const combtuning3: int = 1277;
		private const combtuning4: int = 1356;
		private const combtuning5: int = 1422;
		private const combtuning6: int = 1491;
		private const combtuning7: int = 1557;
		private const combtuning8: int = 1617;
		
		private const allpasstuning1: int = 556;
		private const allpasstuning2: int = 441;
		private const allpasstuning3: int = 341;
		private const allpasstuning4: int = 225;
		
		private const combs: Vector.<ReverbComb> = new Vector.<ReverbComb>( 8 );
		private const allpasses: Vector.<ReverbAllpass> = new Vector.<ReverbAllpass>( 4 );
		
		private var _feedback: Number;
		private var _damp1: Number;
		private var _damp2: Number;
		private var _dry: Number;
		private var _wet1: Number;
		private var _wet2: Number;
		private var _width: Number;

		public function Freeverb( feedback: Number, damp: Number )
		{
			_feedback = feedback;
			_damp1 = damp;
			_damp2 = 1.0 - damp;

			_width = 1.0;
			_dry = .5;
			_wet1 = .5 * ( _width * .5 + 0.5 );
			_wet2 = .5 * ( ( 1.0 - _width ) * .5 );
			
			_init();
		}
		
		public function process( buffer: Vector.<Vector.<Number>> ): void
		{
			var outL: Number;
			var outR: Number;
			var outC: Number;

			var comb: ReverbComb;
			var allpass: ReverbAllpass;
			
			var leBuffer: Vector.<Number> = buffer[0];
			var riBuffer: Vector.<Number> = buffer[1];
			
			var inpL: Number;
			var inpR: Number;
			
			var i: int = 0;
			var n: int = AudioDriver.SIZE;
			var j: int;
			
			for( ; i < n ; ++i )
			{
				outL = outR = 0.0;

				inpL = leBuffer[i];
				inpR = riBuffer[i];

				for( j = 0 ; j < 8 ; ++j )
				{
					comb = combs[j];
					
					//-- LEFT
					outC = comb.buffer[0][ comb.bufferIndex ] + 1e-18 - 1e-18;
					comb.f0 = ( outC * _damp2 ) + ( comb.f0 * _damp1 ) + 1e-18 - 1e-18;
					comb.buffer[0][ comb.bufferIndex ] = inpL * gain + ( comb.f0 * _feedback );
					outL += outC;

					//-- RIGHT
					outC = comb.buffer[1][ comb.bufferIndex ] + 1e-18 - 1e-18;
					comb.f1 = ( outC * _damp2 ) + ( comb.f1 * _damp1 ) + 1e-18 - 1e-18;
					comb.buffer[1][ comb.bufferIndex ] = inpR * gain + ( comb.f1 * _feedback );
					outR += outC;
					
					if( ++comb.bufferIndex == comb.bufferSize )
						comb.bufferIndex = 0;
				}
		
				for( j = 0 ; j < 4 ; ++j )
				{
					allpass = allpasses[j];

					outC = allpass.buffer[1][ allpass.bufferIndex ] + 1e-18 - 1e-18;
					allpass.buffer[0][ allpass.bufferIndex ] = outL + ( outC * .5 );
					outL = outC - outL;
					
					outC = allpass.buffer[0][ allpass.bufferIndex ] + 1e-18 - 1e-18;
					allpass.buffer[1][ allpass.bufferIndex ] = outR + ( outC * .5 );
					outR = outC - outR;
					
					if( ++allpass.bufferIndex == allpass.bufferSize )
						allpass.bufferIndex = 0;
				}

				leBuffer[i] = outL * _wet1 + outR * _wet2 + inpL * _dry;
				riBuffer[i] = outR * _wet1 + outL * _wet2 + inpR * _dry;
			}
		}
		
		private function _init(): void
		{
			combs[0] = new ReverbComb( combtuning1 );
			combs[1] = new ReverbComb( combtuning2 );
			combs[2] = new ReverbComb( combtuning3 );
			combs[3] = new ReverbComb( combtuning4 );
			combs[4] = new ReverbComb( combtuning5 );
			combs[5] = new ReverbComb( combtuning6 );
			combs[6] = new ReverbComb( combtuning7 );
			combs[7] = new ReverbComb( combtuning8 );
			
			allpasses[0] = new ReverbAllpass( allpasstuning1 );
			allpasses[1] = new ReverbAllpass( allpasstuning2 );
			allpasses[2] = new ReverbAllpass( allpasstuning3 );
			allpasses[3] = new ReverbAllpass( allpasstuning4 );
		}
	}
}
	import __AS3__.vec.Vector;
	

class ReverbComb 
{
	public var buffer: Vector.<Vector.<Number>>;
	public var bufferSize: int;
	public var bufferIndex: int;
	public var f0: Number;
	public var f1: Number;
	
	public function ReverbComb( size: int )
	{
		buffer = new Vector.<Vector.<Number>>( 2, true );
		buffer[0] = new Vector.<Number>( size, true );
		buffer[1] = new Vector.<Number>( size, true );
		bufferSize = size;
		bufferIndex = 0;
		f0 = 0.0;
		f1 = 0.0;
	}
}

class ReverbAllpass 
{
	public var buffer: Vector.<Vector.<Number>>;
	public var bufferSize: int;
	public var bufferIndex: int;
	
	public function ReverbAllpass( size: int )
	{
		buffer = new Vector.<Vector.<Number>>( 2, true );
		buffer[0] = new Vector.<Number>( size, true );
		buffer[1] = new Vector.<Number>( size, true );
		bufferSize = size;
		bufferIndex = 0;
	}
}